#include <iostream>
#include <cstdlib>
#include <errno.h>
#include <netinet/ip.h>
#include <netinet/ether.h>
#include <netinet/tcp.h>
#include <stdexcept>

/* Class header */
#include "grabber.h"

Grabber::Grabber() 
	: _descr(NULL), _dispatcher(NULL)
{
}

Grabber::~Grabber()
{
	if (_descr != NULL)
		pcap_close(_descr);
}

void Grabber::init(const char *dev, const char *filter) {
	char errbuf[PCAP_ERRBUF_SIZE];
	
	/* Gather network information */
	pcap_lookupnet(dev, &_net, &_mask, errbuf);

	/* Open device for reading */
	_descr = pcap_open_live(dev, BUFSIZ, 1, -1, errbuf);
	if (_descr == NULL) {
		throw std::runtime_error("Cannot open device!");
		return;
	}
	
	/* Compile filtering program */
	if (pcap_compile(_descr, &_filterProg, filter, 1, _net) == -1) {
		throw std::runtime_error("Wrong filter phrase!");
		return;
	}
	
	/* Turn filter on */
	if (pcap_setfilter(_descr, &_filterProg) == -1) {
		throw std::runtime_error("Error applying filter!");
		return;
	}
	pcap_freecode(&_filterProg);
}

void Grabber::startLoop() {
	if (_dispatcher == NULL) {
		throw std::runtime_error("No dispatcher object specified!");
		return;
	}
	
	if (_descr == NULL) {
		throw std::runtime_error("Run init() before startLoop()");
		return;
	}
	
	pcap_loop(_descr, -1, &Grabber::callback, (uint8_t*) _dispatcher);
}

void Grabber::stopLoop() {
	pcap_breakloop(_descr);
}

void Grabber::setDispatcher(Dispatcher *d) {
	_dispatcher = d;
}

void Grabber::callback(uint8_t *args, const struct pcap_pkthdr *pkthdr,
	const uint8_t *packet)
{
	ip *ipPtr;
	tcphdr *tcpPtr;
	Dispatcher *dispObj;
	uint32_t ipLen, tcpLen, headerLen;
	
	/* Firstly, aquire length of header information */
	ipPtr = (ip*) (packet + ETH_HLEN);
	ipLen = ipPtr->ip_hl * 4;
	
	tcpPtr = (tcphdr*) (packet + ETH_HLEN + ipLen);
#ifdef __FAVOR_BSD
	tcpLen = tcpPtr->th_off * 4;
#else
	tcpLen = tcpPtr->doff * 4;
#endif
	
	headerLen = ETH_HLEN + ipLen + tcpLen;
	
	/* Secondly, run analysis method */
	dispObj = (Dispatcher*) args;
	dispObj->dispatch(packet, headerLen);
}


